/** @file

  Rockchip HDMI/DP Combo PHY with Samsung IP block
  Copyright (c) 2022, Linaro, Ltd. All rights reserved.

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Base.h>
#include <Uefi/UefiBaseType.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/TimerLib.h>
#include <Library/BaseLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/AnalogixDpLib.h>
#include <Library/PWMLib.h>
#include <Library/DrmModes.h>

#include <Library/uboot-env.h>

#define RO_REF_CLK_SEL  GENMASK(11, 10)
#define LC_REF_CLK_SEL  GENMASK(9, 8)
#define PLL_EN          BIT(7)
#define BIAS_EN         BIT(6)
#define BGR_EN          BIT(5)
#define HDPTX_MODE_SEL  BIT(0)
#define PLL_LOCK_DONE   BIT(3)
#define PHY_CLK_RDY     BIT(2)
#define PHY_RDY         BIT(1)
#define SB_RDY          BIT(0)

/* cmn_reg0008 */
#define OVRD_LCPLL_EN  BIT(7)
#define LCPLL_EN       BIT(6)

/* cmn_reg003C */
#define ANA_LCPLL_RESERVED7  BIT(7)

/* cmn_reg003D */
#define OVRD_ROPLL_EN  BIT(7)
#define ROPLL_EN       BIT(6)

/* cmn_reg0046 */
#define ROPLL_ANA_CPP_CTRL_COARSE  GENMASK(7, 4)
#define ROPLL_ANA_CPP_CTRL_FINE    GENMASK(3, 0)

/* cmn_reg0047 */
#define ROPLL_ANA_LPF_C_SEL_COARSE  GENMASK(5, 3)
#define ROPLL_ANA_LPF_C_SEL_FINE    GENMASK(2, 0)

/* cmn_reg004E */
#define ANA_ROPLL_PI_EN  BIT(5)

/* cmn_reg0051 */
#define ROPLL_PMS_MDIV  GENMASK(7, 0)

/* cmn_reg0055 */
#define ROPLL_PMS_MDIV_AFC  GENMASK(7, 0)

/* cmn_reg0059 */
#define ANA_ROPLL_PMS_PDIV    GENMASK(7, 4)
#define ANA_ROPLL_PMS_REFDIV  GENMASK(3, 0)

/* cmn_reg005A */
#define ROPLL_PMS_SDIV_RBR  GENMASK(7, 4)
#define ROPLL_PMS_SDIV_HBR  GENMASK(3, 0)

/* cmn_reg005B */
#define ROPLL_PMS_SDIV_HBR2  GENMASK(7, 4)
#define ROPLL_PMS_SDIV_HBR3  GENMASK(3, 0)

/* cmn_reg005D */
#define OVRD_ROPLL_REF_CLK_SEL  BIT(5)
#define ROPLL_REF_CLK_SEL       GENMASK(4, 3)

/* cmn_reg005E */
#define ANA_ROPLL_SDM_EN              BIT(6)
#define OVRD_ROPLL_SDM_RSTN           BIT(5)
#define ROPLL_SDM_RSTN                BIT(4)
#define ROPLL_SDC_FRACTIONAL_EN_RBR   BIT(3)
#define ROPLL_SDC_FRACTIONAL_EN_HBR   BIT(2)
#define ROPLL_SDC_FRACTIONAL_EN_HBR2  BIT(1)
#define ROPLL_SDC_FRACTIONAL_EN_HBR3  BIT(0)

/* cmn_reg005F */
#define OVRD_ROPLL_SDC_RSTN  BIT(5)
#define ROPLL_SDC_RSTN       BIT(4)

/* cmn_reg0060 */
#define ROPLL_SDM_DENOMINATOR  GENMASK(7, 0)

/* cmn_reg0064 */
#define ROPLL_SDM_NUMERATOR_SIGN_RBR   BIT(3)
#define ROPLL_SDM_NUMERATOR_SIGN_HBR   BIT(2)
#define ROPLL_SDM_NUMERATOR_SIGN_HBR2  BIT(1)
#define ROPLL_SDM_NUMERATOR_SIGN_HBR3  BIT(0)

/* cmn_reg0065 */
#define ROPLL_SDM_NUMERATOR  GENMASK(7, 0)

/* cmn_reg0069 */
#define ROPLL_SDC_N_RBR  GENMASK(2, 0)

/* cmn_reg006A */
#define ROPLL_SDC_N_HBR   GENMASK(5, 3)
#define ROPLL_SDC_N_HBR2  GENMASK(2, 0)

/* cmn_reg006B */
#define ROPLL_SDC_N_HBR3  GENMASK(3, 1)

/* cmn_reg006C */
#define ROPLL_SDC_NUMERATOR  GENMASK(5, 0)

/* cmn_reg0070 */
#define ROPLL_SDC_DENOMINATOR  GENMASK(5, 0)

/* cmn_reg0074 */
#define OVRD_ROPLL_SDC_NDIV_RSTN  BIT(3)
#define ROPLL_SDC_NDIV_RSTN       BIT(2)
#define OVRD_ROPLL_SSC_EN         BIT(1)
#define ROPLL_SSC_EN              BIT(0)

/* cmn_reg0075 */
#define ANA_ROPLL_SSC_FM_DEVIATION  GENMASK(5, 0)

/* cmn_reg0076 */
#define ANA_ROPLL_SSC_FM_FREQ  GENMASK(6, 2)

/* cmn_reg0077 */
#define ANA_ROPLL_SSC_CLK_DIV_SEL  GENMASK(6, 3)

/* cmn_reg0081 */
#define ANA_PLL_CD_TX_SER_RATE_SEL  BIT(3)
#define ANA_PLL_CD_HSCLK_WEST_EN    BIT(1)
#define ANA_PLL_CD_HSCLK_EAST_EN    BIT(0)

/* cmn_reg0082 */
#define ANA_PLL_CD_VREG_GAIN_CTRL  GENMASK(3, 0)

/* cmn_reg0083 */
#define ANA_PLL_CD_VREG_ICTRL  GENMASK(6, 5)

/* cmn_reg0084 */
#define PLL_LCRO_CLK_SEL  BIT(5)

/* cmn_reg0085 */
#define ANA_PLL_SYNC_LOSS_DET_MODE  GENMASK(1, 0)

/* cmn_reg0087 */
#define ANA_PLL_TX_HS_CLK_EN  BIT(2)

/* cmn_reg0095 */
#define DP_TX_LINK_BW  GENMASK(1, 0)

/* cmn_reg0097 */
#define DIG_CLK_SEL  BIT(1)

/* cmn_reg0099 */
#define SSC_EN                GENMASK(7, 6)
#define CMN_ROPLL_ALONE_MODE  BIT(2)

/* cmn_reg009A */
#define HS_SPEED_SEL  BIT(0)

/* cmn_reg009B */
#define LS_SPEED_SEL  BIT(4)

/* sb_reg0102 */
#define OVRD_SB_RXTERM_EN    BIT(5)
#define SB_RXRERM_EN         BIT(4)
#define ANA_SB_RXTERM_OFFSP  GENMASK(3, 0)

/* sb_reg0103 */
#define ANA_SB_RXTERM_OFFSN     GENMASK(6, 3)
#define OVRD_SB_RX_RESCAL_DONE  BIT(1)
#define SB_RX_RESCAL_DONE       BIT(0)

/* sb_reg0104 */
#define OVRD_SB_EN      BIT(5)
#define SB_EN           BIT(4)
#define OVRD_SB_AUX_EN  BIT(1)
#define SB_AUX_EN       BIT(0)

/* sb_reg0105 */
#define ANA_SB_TX_HLVL_PROG  GENMASK(2, 0)

/* sb_reg0106 */
#define ANA_SB_TX_LLVL_PROG  GENMASK(6, 4)

/* sb_reg010D */
#define ANA_SB_DMRX_LPBK_DATA  BIT(4)

/* sb_reg010F */
#define OVRD_SB_VREG_EN        BIT(7)
#define SB_VREG_EN             BIT(6)
#define ANA_SB_VREG_GAIN_CTRL  GENMASK(3, 0)

/* sb_reg0110 */
#define ANA_SB_VREG_OUT_SEL  BIT(1)
#define ANA_SB_VREG_REF_SEL  BIT(0)

/* sb_reg0113 */
#define SB_RX_RCAL_OPT_CODE  GENMASK(5, 4)
#define SB_RX_RTERM_CTRL     GENMASK(3, 0)

/* sb_reg0114 */
#define SB_TG_SB_EN_DELAY_TIME      GENMASK(5, 3)
#define SB_TG_RXTERN_EN_DELAY_TIME  GENMASK(2, 0)

/* sb_reg0115 */
#define SB_READY_DELAY_TIME      GENMASK(5, 3)
#define SB_TG_OSC_EN_DELAY_TIME  GENMASK(2, 0)

/* sb_reg0116 */
#define SB_TG_OSC_EN_TO_AFC_RSTN_DELAT_TIME  GENMASK(6, 4)

/* sb_reg0117 */
#define SB_TG_PLL_CD_VREG_FAST_PULSE_TIME  GENMASK(3, 0)

/* sb_reg0118 */
#define SB_TG_EARC_DMRX_RECVRD_CLK_CNT  GENMASK(7, 0)

/* sb_reg011A */
#define SB_TG_CNT_RUN_NO_7_0  GENMASK(7, 0)

/* sb_reg011B */
#define SB_EARC_SIG_DET_BYPASS  BIT(4)
#define SB_AFC_TOL              GENMASK(3, 0)

/* sb_reg011C */
#define SB_AFC_STB_NUM  GENMASK(3, 0)

/* sb_reg011D */
#define SB_TG_OSC_CNT_MIN  GENMASK(7, 0)

/* sb_reg011E */
#define SB_TG_OSC_CNT_MAX  GENMASK(7, 0)

/* sb_reg011F */
#define SB_PWM_AFC_CTRL  GENMASK(7, 2)
#define SB_RCAL_RSTN     BIT(1)

/* sb_reg0120 */
#define SB_AUX_EN_IN  BIT(7)

/* sb_reg0123 */
#define OVRD_SB_READY  BIT(5)
#define SB_READY       BIT(4)

/* lntop_reg0200 */
#define PROTOCOL_SEL  BIT(2)

/* lntop_reg0206 */
#define DATA_BUS_WIDTH  GENMASK(2, 1)
#define BUS_WIDTH_SEL   BIT(0)

/* lntop_reg0207 */
#define LANE_EN  GENMASK(3, 0)

/* LANE_reg0301 */
#define OVRD_LN_TX_DRV_EI_EN  BIT(7)
#define LN_TX_DRV_EI_EN       BIT(6)

/* LANE_reg0303 */
#define OVRD_LN_TX_DRV_LVL_CTRL  BIT(5)
#define LN_TX_DRV_LVL_CTRL       GENMASK(4, 0)

/* LANE_reg0304 */
#define OVRD_LN_TX_DRV_POST_LVL_CTRL  BIT(4)
#define LN_TX_DRV_POST_LVL_CTRL       GENMASK(3, 0)
#define LN_TX_DRV_POST_LVL_CTRL       GENMASK(3, 0)

/* LANE_reg0305 */
#define OVRD_LN_TX_DRV_PRE_LVL_CTRL  BIT(6)
#define LN_TX_DRV_PRE_LVL_CTRL       GENMASK(5, 2)

/* LANE_reg0306 */
#define LN_ANA_TX_DRV_IDRV_IDN_CTRL  GENMASK(7, 5)
#define LN_ANA_TX_DRV_IDRV_IUP_CTRL  GENMASK(4, 2)
#define LN_ANA_TX_DRV_ACCDRV_EN      BIT(0)

/* LANE_reg0307 */
#define LN_ANA_TX_DRV_ACCDRV_POL_SEL  BIT(6)
#define LN_ANA_TX_DRV_ACCDRV_CTRL     GENMASK(5, 3)

/* LANE_reg030A */
#define LN_ANA_TX_JEQ_EN         BIT(4)
#define LN_TX_JEQ_EVEN_CTRL_RBR  GENMASK(3, 0)

/* LANE_reg030B */
#define LN_TX_JEQ_EVEN_CTRL_HBR   GENMASK(7, 4)
#define LN_TX_JEQ_EVEN_CTRL_HBR2  GENMASK(3, 0)

/* LANE_reg030C */
#define LN_TX_JEQ_EVEN_CTRL_HBR3  GENMASK(7, 4)
#define LN_TX_JEQ_ODD_CTRL_RBR    GENMASK(3, 0)

/* LANE_reg030D */
#define LN_TX_JEQ_ODD_CTRL_HBR   GENMASK(7, 4)
#define LN_TX_JEQ_ODD_CTRL_HBR2  GENMASK(3, 0)

/* LANE_reg030E */
#define LN_TX_JEQ_ODD_CTRL_HBR3  GENMASK(7, 4)

/* LANE_reg0310 */
#define LN_ANA_TX_SYNC_LOSS_DET_MODE  GENMASK(1, 0)

/* LANE_reg0311 */
#define LN_TX_SER_40BIT_EN_RBR   BIT(3)
#define LN_TX_SER_40BIT_EN_HBR   BIT(2)
#define LN_TX_SER_40BIT_EN_HBR2  BIT(1)
#define LN_TX_SER_40BIT_EN_HBR3  BIT(0)

/* LANE_reg0316 */
#define LN_ANA_TX_SER_VREG_GAIN_CTRL  GENMASK(3, 0)

/* LANE_reg031B */
#define LN_ANA_TX_RESERVED  GENMASK(7, 0)

/* LANE_reg031E */
#define LN_POLARITY_INV  BIT(2)

#define LANE_REG(LANE, offset)  (0x400 * (LANE) + (offset))

enum {
  DP_BW_RBR,
  DP_BW_HBR,
  DP_BW_HBR2,
  DP_BW_HBR3,
};

struct TxDrvCtrl {
  UINT8    Tx_Drv_Lvl_Ctrl;
  UINT8    Tx_Drv_Post_Lvl_Ctrl;
  UINT8    Ana_Tx_Drv_Idrv_Idn_Ctrl;
  UINT8    Ana_Tx_Drv_Idrv_Iup_Ctrl;
  UINT8    Ana_Tx_Drv_Accdrv_En;
  UINT8    Ana_Tx_Drv_Accdrv_Ctrl;
};

STATIC CONST struct  TxDrvCtrl TX_DRV_CTRL_RBR[4][4] = {
  /* VOLTAGE swing 0, pre-emphasis 0->3 */
  {
    { 0x1, 0x0, 0x4, 0x6, 0x0, 0x4 },
    { 0x4, 0x3, 0x4, 0x6, 0x0, 0x4 },
    { 0x7, 0x6, 0x4, 0x6, 0x0, 0x4 },
    { 0xd, 0xb, 0x7, 0x7, 0x1, 0x7 },
  },

  /* VOLTAGE swing 1, pre-emphasis 0->2 */
  {
    { 0x4, 0x0, 0x4, 0x6, 0x0, 0x4 },
    { 0xa, 0x5, 0x4, 0x6, 0x0, 0x4 },
    { 0xd, 0x8, 0x7, 0x7, 0x1, 0x7 },
  },

  /* VOLTAGE swing 2, pre-emphasis 0->1 */
  {
    { 0x8, 0x0, 0x4, 0x6, 0x0, 0x4 },
    { 0xd, 0x5, 0x7, 0x7, 0x1, 0x7 },
  },

  /* VOLTAGE swing 3, pre-emphasis 0 */
  {
    { 0xd, 0x0, 0x7, 0x7, 0x1, 0x4 },
  }
};

STATIC CONST struct  TxDrvCtrl TX_DRV_CTRL_HBR[4][4] = {
  /* VOLTAGE swing 0, pre-emphasis 0->3 */
  {
    { 0x2, 0x1, 0x4, 0x6, 0x0, 0x4 },
    { 0x5, 0x4, 0x4, 0x6, 0x0, 0x4 },
    { 0x9, 0x8, 0x4, 0x6, 0x0, 0x4 },
    { 0xd, 0xb, 0x7, 0x7, 0x1, 0x7 },
  },

  /* VOLTAGE swing 1, pre-emphasis 0->2 */
  {
    { 0x6, 0x1, 0x4, 0x6, 0x0, 0x4 },
    { 0xb, 0x6, 0x4, 0x6, 0x0, 0x4 },
    { 0xd, 0x8, 0x7, 0x7, 0x1, 0x7 },
  },

  /* VOLTAGE swing 2, pre-emphasis 0->1 */
  {
    { 0x9, 0x1, 0x4, 0x6, 0x0, 0x4 },
    { 0xd, 0x6, 0x7, 0x7, 0x1, 0x7 },
  },

  /* VOLTAGE swing 3, pre-emphasis 0 */
  {
    { 0xd, 0x1, 0x7, 0x7, 0x1, 0x4 },
  }
};

STATIC CONST struct  TxDrvCtrl TX_DRV_CTRL_HBR2[4][4] = {
  /* VOLTAGE swing 0, pre-emphasis 0->3 */
  {
    { 0x2, 0x1, 0x4, 0x6, 0x0, 0x4 },
    { 0x5, 0x4, 0x4, 0x6, 0x0, 0x4 },
    { 0x9, 0x8, 0x4, 0x6, 0x1, 0x4 },
    { 0xd, 0xb, 0x7, 0x7, 0x1, 0x7 },
  },

  /* VOLTAGE swing 1, pre-emphasis 0->2 */
  {
    { 0x6, 0x1, 0x4, 0x6, 0x0, 0x4 },
    { 0xc, 0x7, 0x4, 0x6, 0x0, 0x4 },
    { 0xd, 0x8, 0x7, 0x7, 0x1, 0x7 },
  },

  /* VOLTAGE swing 2, pre-emphasis 0->1 */
  {
    { 0x9, 0x1, 0x4, 0x6, 0x0, 0x4 },
    { 0xd, 0x6, 0x7, 0x7, 0x1, 0x7 },
  },

  /* VOLTAGE swing 3, pre-emphasis 0 */
  {
    { 0xd, 0x0, 0x7, 0x7, 0x1, 0x4 },
  }
};

INLINE
VOID
PhyWrite (
  OUT struct RockchipHdptxPhy  *Hdptx,
  UINTN                        Reg,
  UINTN                        Val
  )
{
  UINT32  Shift;

  if (!Hdptx->Id) {
    Shift = EDP0TX_PHY_BASE;
  } else {
    Shift = EDP1TX_PHY_BASE;
  }

  MmioWrite32 (Shift + Reg, Val);
}

INLINE
UINTN
PhyRead (
  OUT struct RockchipHdptxPhy  *Hdptx,
  UINTN                        Reg
  )
{
  UINT32  Shift;

  if (!Hdptx->Id) {
    Shift = EDP0TX_PHY_BASE;
  } else {
    Shift = EDP1TX_PHY_BASE;
  }

  return MmioRead32 (Shift + Reg);
}

STATIC
VOID
PhyUpdateBits (
  OUT struct RockchipHdptxPhy  *Hdptx,
  UINTN                        Reg,
  UINTN                        Mask,
  UINTN                        Val
  )
{
  UINTN  Orig, Tmp;

  Orig = PhyRead (Hdptx, Reg);
  Tmp  = Orig & ~Mask;
  Tmp |= Val & Mask;
  PhyWrite (Hdptx, Reg, Tmp);
}

STATIC
VOID
GrfWrite (
  OUT struct RockchipHdptxPhy  *Hdptx,
  UINTN                        Reg,
  UINTN                        Mask,
  UINTN                        Val
  )
{
  UINT32  TempVal = 0;
  UINT32  Shift;

  if (!Hdptx->Id) {
    Shift = HDPTXPHY0_GRF_BASE;
  } else {
    Shift = HDPTXPHY1_GRF_BASE;
  }

  TempVal = (Mask << 16) | (Val & Mask);
  MmioWrite32 (Shift + Reg, TempVal);
}

STATIC
VOID
RockchipHdptxPhyDpPllInit (
  struct RockchipHdptxPhy  *Hdptx
  )
{
  PhyUpdateBits (
    Hdptx,
    0x20,
    OVRD_LCPLL_EN |
    LCPLL_EN,
    0x80
    );
  PhyWrite (Hdptx, 0xf4, 0xC0);
  PhyUpdateBits (
    Hdptx,
    0x0138,
    ANA_ROPLL_PI_EN,
    0x20
    );
  PhyWrite (Hdptx, 0x0144, 0x87);
  PhyWrite (Hdptx, 0x0148, 0x71);
  PhyWrite (Hdptx, 0x014c, 0x71);
  PhyWrite (Hdptx, 0x0154, 0x87);
  PhyWrite (Hdptx, 0x0158, 0x71);
  PhyWrite (Hdptx, 0x015c, 0x71);
  PhyWrite (Hdptx, 0x0164, 0x11);
  PhyWrite (Hdptx, 0x0168, 0x31);
  PhyUpdateBits (Hdptx, 0x016c, ROPLL_PMS_SDIV_HBR2, 0x0);
  PhyWrite (Hdptx, 0x0178, 0x7f);
  PhyWrite (Hdptx, 0x017c, 0x31);
  PhyWrite (Hdptx, 0x0180, 0x21);
  PhyWrite (Hdptx, 0x0184, 0x27);
  PhyWrite (Hdptx, 0x0188, 0x27);
  PhyWrite (Hdptx, 0x0190, 0x7);
  PhyWrite (Hdptx, 0x0194, 0x0);
  PhyWrite (Hdptx, 0x0198, 0xd);
  PhyWrite (Hdptx, 0x019c, 0xd);
  PhyWrite (Hdptx, 0x01a4, 0x2);
  PhyWrite (Hdptx, 0x01a8, 0x09);
  PhyWrite (Hdptx, 0x01b0, 0x3);
  PhyWrite (Hdptx, 0x01b4, 0x7);
  PhyWrite (Hdptx, 0x01b8, 0x7);
  PhyWrite (Hdptx, 0x01c0, 0x8);
  PhyWrite (Hdptx, 0x01c4, 0x18);
  PhyWrite (Hdptx, 0x01c8, 0x18);
  PhyWrite (Hdptx, 0x01d0, 0x0f);
  PhyWrite (Hdptx, 0x01dc, 0x08);
  PhyWrite (Hdptx, 0x0118, 0xee);
  PhyWrite (Hdptx, 0x011c, 0x24);
  PhyWrite (Hdptx, 0x0204, 0x01);
  PhyUpdateBits (Hdptx, 0x025c, DIG_CLK_SEL, 0x2);
  PhyUpdateBits (Hdptx, 0x021c, ANA_PLL_TX_HS_CLK_EN, 0x4);
  PhyUpdateBits (
    Hdptx,
    0x0204,
    ANA_PLL_CD_HSCLK_EAST_EN |
    ANA_PLL_CD_HSCLK_WEST_EN,
    0x1
    );
  PhyWrite (Hdptx, 0x0264, 0x84);
  PhyWrite (Hdptx, 0x0208, 0x04);
  PhyUpdateBits (
    Hdptx,
    0x00f0,
    ANA_LCPLL_RESERVED7,
    0x80
    );
  PhyWrite (Hdptx, 0x020c, 0x24);
  PhyWrite (Hdptx, 0x0214, 0x03);
  PhyUpdateBits (
    Hdptx,
    0x0210,
    PLL_LCRO_CLK_SEL,
    0x20
    );
  PhyUpdateBits (
    Hdptx,
    0x0268,
    HS_SPEED_SEL,
    0x1
    );
  PhyUpdateBits (
    Hdptx,
    0x026c,
    LS_SPEED_SEL,
    0x10
    );
}

STATIC
INTN
RockchipHdptxPhyDpAuxInit (
  struct RockchipHdptxPhy  *Hdptx
  )
{
  PhyUpdateBits (Hdptx, 0x0414, ANA_SB_TX_HLVL_PROG, 0x7);
  PhyUpdateBits (Hdptx, 0x0418, ANA_SB_TX_LLVL_PROG, 0x70);
  PhyWrite (Hdptx, 0x044c, 0x13);
  PhyWrite (Hdptx, 0x0450, 0x12);
  PhyWrite (Hdptx, 0x0454, 0x12);
  PhyWrite (Hdptx, 0x0458, 0x20);
  PhyWrite (Hdptx, 0x045c, 0x04);
  PhyWrite (Hdptx, 0x0460, 0x0a);
  PhyWrite (Hdptx, 0x0468, 0x03);
  PhyWrite (Hdptx, 0x046c, 0x13);
  PhyWrite (Hdptx, 0x0470, 0x04);
  PhyUpdateBits (Hdptx, 0x0474, SB_TG_OSC_CNT_MIN, 0x67);
  PhyUpdateBits (Hdptx, 0x0478, SB_TG_OSC_CNT_MAX, 0x6a);
  PhyWrite (Hdptx, 0x047c, 0x16);
  PhyUpdateBits (Hdptx, 0x0434, ANA_SB_DMRX_LPBK_DATA, 0x10);
  PhyWrite (Hdptx, 0x0440, 0x03);
  PhyUpdateBits (Hdptx, 0x043c, ANA_SB_VREG_GAIN_CTRL, 0x0);
  PhyUpdateBits (Hdptx, 0x0408, ANA_SB_RXTERM_OFFSP, 0x3);
  PhyWrite (Hdptx, 0x040c, 0x1b);
  PhyUpdateBits (Hdptx, 0x047c, SB_RCAL_RSTN, 0x2);
  PhyUpdateBits (Hdptx, 0x0410, SB_AUX_EN, 0x0);
  PhyUpdateBits (Hdptx, 0x0480, SB_AUX_EN_IN, 0x80);
  PhyUpdateBits (Hdptx, 0x0410, OVRD_SB_EN, 0x20);
  PhyUpdateBits (Hdptx, 0x0408, OVRD_SB_RXTERM_EN, 0x20);
  PhyUpdateBits (Hdptx, 0x0410, OVRD_SB_AUX_EN, 0x2);

  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, BGR_EN, 0x21);
  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, BIAS_EN, 0x61);
  MicroSecondDelay (10);

  PhyUpdateBits (Hdptx, 0x0410, SB_EN, 0x10);
  PhyUpdateBits (Hdptx, 0x0408, SB_RXRERM_EN, 0x10);
  PhyWrite (Hdptx, 0x043c, 0xc0);
  PhyUpdateBits (Hdptx, 0x0410, SB_AUX_EN, 0x1);
  NanoSecondDelay (100000);
  GrfWrite (Hdptx, HDPTXPHY_GRF_STATUS0, SB_RDY, 0x1);
  MicroSecondDelay (10);
  return 0;
}

STATIC
VOID
RockchipHdptxPhyReset (
  struct RockchipHdptxPhy  *Hdptx
  )
{
  UINT32  LANE;

  for (LANE = 0; LANE < 4; LANE++) {
    PhyUpdateBits (
      Hdptx,
      LANE_REG (LANE, 0x0c04),
      OVRD_LN_TX_DRV_EI_EN | LN_TX_DRV_EI_EN,
      0x80
      );
  }

  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, PLL_EN, 0x0);
  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, BIAS_EN, 0x0);
  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, BGR_EN, 0x0);
}

VOID
RockchipHdptxPhyInit (
  OUT struct RockchipHdptxPhy  *Hdptx
  )
{
  RockchipHdptxPhyReset (Hdptx);

  MmioWrite32 (0xFD7F0A0C, 0xFFFF8000);

  /* Todo lane-polarity-invert  */
  PhyWrite (Hdptx, 0x0C78, 0x00);
  PhyWrite (Hdptx, 0x1078, 0x00);
  PhyWrite (Hdptx, 0x1478, 0x00);
  PhyWrite (Hdptx, 0x1878, 0x00);

  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, HDPTX_MODE_SEL, 0x01);

  /* Todo RockchipHdptxPhyPowerOn */
  PhyWrite (Hdptx, 0x0800, 0x00);
  PhyWrite (Hdptx, 0x0818, 0x02);

  RockchipHdptxPhyDpPllInit (Hdptx);
  RockchipHdptxPhyDpAuxInit (Hdptx);
  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, BGR_EN, 0x21);
  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, BIAS_EN, 0x61);
  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, PLL_EN, 0xe1);
  MmioWrite32 (0xFD7F0A0C, 0xFFFF8000);
  MmioWrite32 (0xFD7F0A0C, 0xFFFF0000);
  MmioWrite32 (0xFD7F0A10, 0xFFFF0000);
}

INTN
RockchipHdptxPhySetRate (
  OUT struct RockchipHdptxPhy    *Hdptx,
  OUT struct PhyConfigureOptsDp  *Dp
  )
{
  UINT32  BW;

  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, PLL_EN, 0x0);
  PhyUpdateBits (Hdptx, 0x081c, LANE_EN, 0x0);
  DEBUG ((
    DEBUG_INFO,
    "[DRIVER]enter %a rate %d Dp->SSC %d \n",
    __func__,
    Dp->LINKRATE,
    Dp->SSC
    ));

  switch (Dp->LINKRATE) {
    case 1620:
      BW = DP_BW_RBR;
      break;
    case 2700:
      BW = DP_BW_HBR;
      break;
    case 5400:
      BW = DP_BW_HBR2;
      break;
    default:
      return -EINVAL;
  }

  PhyUpdateBits (Hdptx, 0x0254, DP_TX_LINK_BW, BW);

  if (Dp->SSC) {
    PhyUpdateBits (
      Hdptx,
      0x01d0,
      OVRD_ROPLL_SSC_EN | ROPLL_SSC_EN,
      0x3
      );
    PhyUpdateBits (
      Hdptx,
      0x01d4,
      ANA_ROPLL_SSC_FM_DEVIATION,
      0xc
      );
    PhyUpdateBits (
      Hdptx,
      0x01d8,
      ANA_ROPLL_SSC_FM_FREQ,
      0x7c
      );
    PhyUpdateBits (
      Hdptx,
      0x0264,
      SSC_EN,
      0x80
      );
  } else {
    PhyUpdateBits (
      Hdptx,
      0x01d0,
      OVRD_ROPLL_SSC_EN | ROPLL_SSC_EN,
      0x3
      );
    PhyUpdateBits (
      Hdptx,
      0x01d4,
      ANA_ROPLL_SSC_FM_DEVIATION,
      0xe
      );
    PhyUpdateBits (
      Hdptx,
      0x01d8,
      ANA_ROPLL_SSC_FM_FREQ,
      0xd
      );
    PhyUpdateBits (
      Hdptx,
      0x0264,
      SSC_EN,
      0x20
      );
    PhyUpdateBits (
      Hdptx,
      0x01d8,
      ANA_ROPLL_SSC_FM_FREQ,
      0x30
      );
  }

  PhyWrite (Hdptx, 0x081C, 0x03);
  PhyWrite (Hdptx, 0x0C0C, 0x26);
  PhyWrite (Hdptx, 0x0C10, 0x11);
  PhyWrite (Hdptx, 0x100C, 0x26);
  PhyWrite (Hdptx, 0x101C, 0x60);
  PhyWrite (Hdptx, 0x140C, 0x00);
  PhyWrite (Hdptx, 0x1410, 0x00);
  PhyWrite (Hdptx, 0x141C, 0x00);
  PhyWrite (Hdptx, 0x1428, 0x00);
  PhyWrite (Hdptx, 0x142C, 0x00);
  PhyWrite (Hdptx, 0x1434, 0x00);
  PhyWrite (Hdptx, 0x1444, 0x00);
  PhyWrite (Hdptx, 0x1458, 0x00);
  PhyWrite (Hdptx, 0x146C, 0x00);
  PhyWrite (Hdptx, 0x180C, 0x00);
  PhyWrite (Hdptx, 0x1810, 0x00);
  PhyWrite (Hdptx, 0x181C, 0x00);
  PhyWrite (Hdptx, 0x1828, 0x00);
  PhyWrite (Hdptx, 0x182C, 0x00);
  PhyWrite (Hdptx, 0x1834, 0x00);
  PhyWrite (Hdptx, 0x1844, 0x00);
  PhyWrite (Hdptx, 0x1858, 0x00);
  PhyWrite (Hdptx, 0x186C, 0x00);
  GrfWrite (Hdptx, HDPTXPHY_GRF_CON0, PLL_EN, 0x80);
  GrfWrite (Hdptx, HDPTXPHY_GRF_STATUS0, PLL_LOCK_DONE, 0x8);
  PhyUpdateBits (
    Hdptx,
    0x081c,
    LANE_EN,
    GENMASK (Dp->LANES - 1, 0)
    );
  NanoSecondDelay (100000);
  GrfWrite (Hdptx, HDPTXPHY_GRF_STATUS0, PHY_RDY, 0x2);
  NanoSecondDelay (10000);
  return 0;
}

VOID
RockchipHdptxPhySetVoltage (
  OUT struct RockchipHdptxPhy    *Hdptx,
  OUT struct PhyConfigureOptsDp  *Dp,
  UINT8                          LANE
  )
{
  CONST struct TxDrvCtrl  *ctrl;

  DEBUG ((
    DEBUG_INFO,
    "[DRIVER]enter %a Dp->LINKRATE %d \n",
    __func__,
    Dp->LINKRATE
    ));

  switch (Dp->LINKRATE) {
    case 1620:
      ctrl = &TX_DRV_CTRL_RBR[Dp->VOLTAGE[LANE]][Dp->PRE[LANE]];
      PhyUpdateBits (
        Hdptx,
        LANE_REG (LANE, 0x0c44),
        LN_TX_SER_40BIT_EN_RBR,
        0x8
        );
      break;
    case 2700:
      ctrl = &TX_DRV_CTRL_HBR[Dp->VOLTAGE[LANE]][Dp->PRE[LANE]];
      PhyUpdateBits (
        Hdptx,
        LANE_REG (LANE, 0x0c2c),
        LN_TX_JEQ_EVEN_CTRL_HBR,
        0x70
        );
      PhyUpdateBits (
        Hdptx,
        LANE_REG (LANE, 0x0c34),
        LN_TX_JEQ_ODD_CTRL_HBR,
        0x70
        );
      PhyUpdateBits (
        Hdptx,
        LANE_REG (LANE, 0x0c44),
        LN_TX_SER_40BIT_EN_HBR,
        0x4
        );
      break;
    case 5400:
    default:
      ctrl = &TX_DRV_CTRL_HBR2[Dp->VOLTAGE[LANE]][Dp->PRE[LANE]];
      PhyUpdateBits (
        Hdptx,
        LANE_REG (LANE, 0x0c2c),
        LN_TX_JEQ_EVEN_CTRL_HBR2,
        0x7
        );
      PhyUpdateBits (
        Hdptx,
        LANE_REG (LANE, 0x0c34),
        LN_TX_JEQ_ODD_CTRL_HBR2,
        0x7
        );
      PhyUpdateBits (
        Hdptx,
        LANE_REG (LANE, 0x0c44),
        LN_TX_SER_40BIT_EN_HBR2,
        0x2
        );
      break;
  }

  PhyUpdateBits (
    Hdptx,
    LANE_REG (LANE, 0x0c0c),
    OVRD_LN_TX_DRV_LVL_CTRL | LN_TX_DRV_LVL_CTRL,
    0x2d
    );
  PhyUpdateBits (
    Hdptx,
    LANE_REG (LANE, 0x0c10),
    OVRD_LN_TX_DRV_POST_LVL_CTRL | LN_TX_DRV_POST_LVL_CTRL,
    0x10 | ctrl->Tx_Drv_Post_Lvl_Ctrl
    );
  PhyUpdateBits (
    Hdptx,
    LANE_REG (LANE, 0x0c18),
    LN_ANA_TX_DRV_IDRV_IDN_CTRL |
    LN_ANA_TX_DRV_IDRV_IUP_CTRL |
    LN_ANA_TX_DRV_ACCDRV_EN,
    0x00
    );
  PhyUpdateBits (
    Hdptx,
    LANE_REG (LANE, 0x0c1c),
    LN_ANA_TX_DRV_ACCDRV_POL_SEL | LN_ANA_TX_DRV_ACCDRV_CTRL,
    0x40 | ctrl->Ana_Tx_Drv_Accdrv_Ctrl
    );
  PhyUpdateBits (
    Hdptx,
    LANE_REG (LANE, 0x0c6c),
    LN_ANA_TX_RESERVED,
    0x1
    );
  PhyUpdateBits (
    Hdptx,
    LANE_REG (LANE, 0x0c58),
    LN_ANA_TX_SER_VREG_GAIN_CTRL,
    0x2
    );
  PhyUpdateBits (
    Hdptx,
    LANE_REG (LANE, 0x0c40),
    LN_ANA_TX_SYNC_LOSS_DET_MODE,
    0x3
    );
}

INTN
RockchipHdptxPhySetVoltages (
  OUT struct RockchipHdptxPhy    *Hdptx,
  OUT struct PhyConfigureOptsDp  *Dp
  )
{
  UINT8  LANE;

  for (LANE = 0; LANE < Dp->LANES; LANE++) {
    RockchipHdptxPhySetVoltage (Hdptx, Dp, LANE);
  }

  return 0;
}

STATIC
INTN
RockchipHdptxPhyVerifyConfig (
  OUT struct RockchipHdptxPhy    *Hdptx,
  OUT struct PhyConfigureOptsDp  *Dp
  )
{
  INTN  i;

  if (Dp->SETRATE) {
    switch (Dp->LINKRATE) {
      case 1620:
      case 2700:
      case 5400:
        break;
      default:
        return -EINVAL;
    }
  }

  switch (Dp->LANES) {
    case 1:
    case 2:
    case 4:
      break;
    default:
      return -EINVAL;
  }

  if (Dp->SETVOLTAGES) {
    for (i = 0; i < Dp->LANES; i++) {
      if ((Dp->VOLTAGE[i] > 3) || (Dp->PRE[i] > 3)) {
        return -EINVAL;
      }

      if (Dp->VOLTAGE[i] + Dp->PRE[i] > 3) {
        return -EINVAL;
      }
    }
  }

  return 0;
}

INTN
RockchipHdptxPhyConfigure (
  OUT struct RockchipHdptxPhy    *Hdptx,
  OUT struct PhyConfigureOptsDp  *Dp
  )
{
  INTN  Ret;

  Ret = RockchipHdptxPhyVerifyConfig (Hdptx, Dp);
  if (Ret) {
    DEBUG ((DEBUG_WARN, "Invalid params for phy configure\n"));
    return Ret;
  }

  if (Dp->SETRATE) {
    Ret = RockchipHdptxPhySetRate (Hdptx, Dp);
    if (Ret) {
      DEBUG ((DEBUG_WARN, "Failed to set rate: %d\n", Ret));
      return Ret;
    }
  }

  if (Dp->SETVOLTAGES) {
    Ret = RockchipHdptxPhySetVoltages (Hdptx, Dp);
    if (Ret) {
      DEBUG ((DEBUG_WARN, "Failed to set Voltages: %d\n", Ret));
      return Ret;
    }
  }

  return 0;
}
